#ifndef STRING_SUB_RANGE_H
#define STRING_SUB_RANGE_H

#include <string>
#include <utility>
#include <iterator>
#include <algorithm>
#include <iostream>
#include <boost/detail/iterator.hpp>

// adapted from iterator_range which unfortunately did not do what I wanted, not being
// a container
namespace boost_helper {
        template<typename STR_TYPE> 
        class t_substr_ref
        {
		public:
			typedef typename STR_TYPE::size_type str_size_type;
            //! this type
            typedef t_substr_ref<STR_TYPE> type;
			typedef typename STR_TYPE::iterator IteratorT;
            //! Encapsulated value type
            typedef BOOST_STRING_TYPENAME boost::detail::
                iterator_traits<IteratorT>::value_type value_type;
            //! Reference type
            typedef BOOST_STRING_TYPENAME boost::detail::
                iterator_traits<IteratorT>::reference reference;
            //! Difference type
            typedef BOOST_STRING_TYPENAME boost::detail::
                iterator_traits<IteratorT>::difference_type difference_type;
            //! Size type
            typedef BOOST_STRING_TYPENAME boost::detail::
                iterator_traits<IteratorT>::difference_type size_type;
            
            //! const_iterator type
            /*! 
                There is no distinction between const_iterator and iterator.
                These typedefs are provides to fulfill container interface
            */ 
            typedef IteratorT const_iterator;
            //! iterator type
            typedef IteratorT iterator;

			t_substr_ref(STR_TYPE& str, str_size_type pos = 0, 
								   str_size_type len = STR_TYPE::npos)
				:m_str(str), m_pos(pos), m_len(len), m_orig_len(str.length())
			{
				assert(m_pos < m_str.length() || m_str.empty());
				if (m_len == STR_TYPE::npos)
					m_len = m_orig_len;
				assert(m_len <= m_orig_len);
			}

            //! Copy constructor
            t_substr_ref( const t_substr_ref& Other ) :
                m_str(Other.m_str), m_pos(Other.m_pos), m_len(Other.m_len),
				m_orig_len(Other.m_orig_len) {}

            //! Templated copy constructor
            /*!
                This constructor is provided to allow conversion between
                const and mutable iterator instances of this class template
            */
            template< typename OTHER_STR_TYPE >
            t_substr_ref( const t_substr_ref<OTHER_STR_TYPE>& Other ) :
                m_str(Other.m_str), m_pos(Other.m_pos), m_len(Other.m_len),
				m_orig_len(Other.m_orig_len) {}

            //! Assignment operator
            t_substr_ref& operator=( const t_substr_ref& Other )
            {
                m_str = Other.m_str;
				m_pos = Other.m_pos;
				m_len = Other.m_len;
				m_orig_len = Other.m_orig_len;
                return *this;
            }

            //! Assignment operator ( templated version )
            template< typename OTHER_STR_TYPE >
            t_substr_ref& operator=( const t_substr_ref<OTHER_STR_TYPE>& Other )
            {
				m_str = Other.m_str;
				m_pos = Other.m_pos;
				m_len = Other.m_len;
				m_orig_len = Other.m_orig_len;
                return *this;
            }
            
            //! Comparison operator ( equal )
            /*! 
                Compare operands for equality
            */
            template< typename OTHER_STR_TYPE > 
            bool operator==( const t_substr_ref<OTHER_STR_TYPE>& Other ) const
            {
				return  
						m_str == Other.m_str &&
						m_pos == Other.m_pos &&
						m_len == Other.m_len &&
						m_orig_len == Other.m_orig_len;

            }

            //! Comparison operator ( not-equal )
            /*! 
                Compare operands for non-equality
            */
            template< typename OTHER_STR_TYPE > 
            bool operator!=( const t_substr_ref<OTHER_STR_TYPE>& Other ) const
            {
				return  
						m_str != Other.m_str ||
						m_pos != Other.m_pos ||
						m_len != Other.m_len ||
						m_orig_len != Other.m_orig_len;
            }

            //! begin access
            /*! 
                Retrieve the begin iterator
            */
            IteratorT begin() const 
            { 
				assert(m_pos < m_str.length() || m_str.empty());
                return m_str.begin() + m_pos; 
            }

            //! end access
            /*!
                Retrieve the end iterator
            */
            IteratorT end() const 
            { 
				str_size_type len = m_len + m_str.length() - m_orig_len;
				//assert(len >= 0);
				// len is unsigned and alway >= 0  anyway
				if (len > m_str.length())
					std::cerr
								<< "len      = "	<< m_len + m_str.length() - m_orig_len      << ","
								<< "m_str      = "	<< m_str      << ","
								<< "m_pos      = "	<< m_pos      << ","
								<< "m_len      = "	<< m_len      << ","
								<< "m_orig_len = "	<< m_orig_len << "\n";
				assert(len <= m_str.length());
				return m_str.begin() + len;
            } 

            //! Empty container test
            /*!
                Test whether the range is empty
            */
            bool empty() const 
            { 
				str_size_type len = m_len + m_str.length() - m_orig_len;
				assert(len >= 0);
                return len > 0; 
            }
            
            //! Size of the range
            /*!
                Retrieve the size of the range
            */
            difference_type size() const
            { 
				assert(m_len + m_str.length() - m_orig_len >= 0);
                return m_len + m_str.length() - m_orig_len; 
            }

            //! Swap
            /*!
                Swap two ranges
            */
            void swap( t_substr_ref& Other )
            {
                std::swap(m_len		, Other.m_len     );
                std::swap(m_orig_len, Other.m_orig_len);
				std::swap(m_pos		, Other.m_pos     );
				std::swap(m_str		, Other.m_str     );
            }
            

			template< typename OTHER_ITER > 
			inline void insert(
								IteratorT At,
								OTHER_ITER Begin,
								OTHER_ITER End )
			{
				m_str.insert( At, Begin, End );
			}



			// Erase a range in the sequence
			/*
				Returns the iterator pointing just after the erase subrange
			*/
			inline IteratorT erase(	IteratorT From,	IteratorT To )
			{
				return m_str.erase( From, To );
			}


		private:
				STR_TYPE&			m_str;
				str_size_type			m_pos;
				str_size_type			m_len;
				str_size_type			m_orig_len;
        };

//  iterator range free-standing operators ---------------------------//

        //! t_substr_ref output operator
        /*!
            Output the range to an ostream. Elements are outputed
            in a sequence without separators.
        */
        template< typename STR_TYPE, typename Elem, typename Traits >
        std::basic_ostream<Elem,Traits>& operator<<( 
            std::basic_ostream<Elem, Traits>& Os,
            const t_substr_ref<STR_TYPE>& Range )
        {
            std::copy(Range.begin(), Range.end(), std::ostream_iterator<Elem>(Os));

            return Os;
        }


//  iterator range utilities -----------------------------------------//

        //! iterator_range construct helper 
        /*!
            Construct an \c iterator_range from a pair of iterators

            \param Begin A begin iterator
            \param End An end iterator
            \return iterator_range object
        */
        template< typename STR_TYPE >
        inline t_substr_ref< STR_TYPE > make_substr_ref(STR_TYPE& str, 
														typename STR_TYPE::str_size_type pos_ = 0, 
														typename STR_TYPE::str_size_type len = STR_TYPE::npos)
        {   
            return t_substr_ref< STR_TYPE > ( str, pos_, len);
        }

} // namespace boost_helper


#endif  // STRING_SUB_RANGE_H
